package mornd.test6;

import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @author: mornd
 * @dateTime: 2023/5/6 - 23:51
 * 动态通知调用
 * 有参数绑定的通知调用时还需要切点，对参数进行匹配及绑定
 * 有参数绑定的通知较复杂，且性能比无参数绑定要低
 */
public class Demo {
    public static void main(String[] args) throws Throwable {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(Config.class);
        context.refresh();

        // 通知后者处理器
        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method findEligibleAdvisors = AbstractAdvisorAutoProxyCreator.class.getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
        findEligibleAdvisors.setAccessible(true);
        List<Advisor> advisors = (List<Advisor>) findEligibleAdvisors.invoke(creator, Target.class, "target");

        Target target = new Target();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisors(advisors);
        Object proxy = factory.getProxy();

        //((Target) proxy).foo(123);

        List<Object> methodInterceptorList = factory.getInterceptorsAndDynamicInterceptionAdvice(
                Target.class.getDeclaredMethod("foo", int.class), Target.class);

        for (Object o : methodInterceptorList) {
//            System.out.println(o);
            showDetail(o);
        }

        // 调用
        ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(
                proxy, target, Target.class.getDeclaredMethod("foo", int.class),
                new Object[] {2}, Target.class, methodInterceptorList) {};

        Object result = invocation.proceed();

        context.close();
    }

    public static void showDetail(Object o) {
        try {
            Class clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
            if(clazz.isInstance(o)) {
                Field f1 = clazz.getDeclaredField("interceptor");
                f1.setAccessible(true);
                MethodInterceptor interceptor = (MethodInterceptor) f1.get(o);

                Field f2 = clazz.getDeclaredField("methodMatcher");
                f2.setAccessible(true);
                MethodMatcher methodMatcher = (MethodMatcher) f2.get(o);
                System.out.println("环绕通知和切点：");
                System.out.println("\t通知：" + interceptor);
                System.out.println("\t切点：" + methodMatcher);
            } else {
                System.out.println("普通通知：" + o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Aspect
    static class MyAspect {

        @Before("execution(* foo(..))")
        public void before() {
            System.out.println("MyAspect before...");
        }

        @Before("execution(* foo(..)) && args(p)")
        public void before2(int p) {
            System.out.printf("MyAspect before2(%d)%n", p);
        }
    }

    static class Target {
        public void foo(int p) {
            System.out.printf("Target foo(%d)%n", p);
        }
    }

    @Configuration
    static class Config {
        @Bean
        public AnnotationAwareAspectJAutoProxyCreator autoProxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean
        public MyAspect aspect() {
            return new MyAspect();
        }
    }
}
